/**
 * Copyright 2012 ibiblio
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.ibiblio.terasaur.taglib

import org.ibiblio.terasaur.ui.DataFormatter
import org.ibiblio.terasaur.item.BrowseService
import org.ibiblio.terasaur.ui.StringCleaner
import org.ibiblio.terasaur.item.Item
import org.ibiblio.terasaur.search.SolrItem
import org.apache.solr.common.SolrDocument
import org.ibiblio.terasaur.search.DateHelper
import org.joda.time.DateTime

class ItemTagLib {
    static namespace = 'ts'
    static DataFormatter _dataFormatter
    static StringCleaner _stringCleaner
    def browseService // injected
    def springSecurityService // injected

    /**
     * Generate basic info string describing the download count and total
     * size for an item.
     */
    def downloadInfo = { attrs ->
        def item
        if (attrs && attrs.containsKey('item')) {
            item = attrs.item
        } else {
            return
        }

        Long tCount = _getTorrentCount(item)
        if (tCount == 0) {
            out << "No downloads"
        } else {
            if (tCount == 1) {
                out << "1 download, "
            } else {
                out << tCount << " downloads, "
            }

            Long total = _getTorrentTotalBytes(item)
            def formatter = _getDataFormatter()
            def formattedBytes = formatter.formatBytes(total, 2)
            out << formattedBytes
        }
    }

    private int _getTorrentCount(Item item) {
        int tCount = 0
        DateTime now = DateHelper.now()
        item.torrents.each {torrent->
            if (torrent.published.isBefore(now)) {
                ++tCount
            }
        }
        return tCount
    }

    private int _getTorrentCount(SolrItem item) {
        return item.torrentCount
    }

    private Long _getTorrentTotalBytes(Item item) {
        Long total = 0
        DateTime now = DateHelper.now()
        item.torrents.each {torrent->
            if (torrent.published.isBefore(now)) {
                total += torrent.size
            }
        }
        return total
    }

    private Long _getTorrentTotalBytes(SolrItem item) {
        return item.torrentBytes
    }

    def popularItems = { attrs ->
        int offset
        int max
        String tag

        if (attrs && attrs.containsKey('offset')) {
            offset = attrs.int('offset')
        } else {
            offset = 0
        }
        if (attrs && attrs.containsKey('max')) {
            max = attrs.int('max')
        } else {
            max = 10
        }
        if (attrs && attrs.containsKey('tag')) {
            tag = attrs['tag']
        } else {
            tag = 'li'
        }

        def listParams = [offset:offset, max:max]
        try {
            Map popularItems = browseService.popular(listParams)
            for (itemInstance in popularItems['results']) {
                out << '<' << tag << '>'
                out << _getItemLink([action: 'show'], itemInstance, itemInstance.title)
                out << '</' << tag << '>'
            }
        } catch (Exception e) {
            log.error(e)
        }
    }

    def itemLink = { attrs, body ->
        if (!(attrs && attrs.containsKey('item'))) {
            return
        }

        Map params = [:]
        if (attrs['action']) {
            params.action = attrs['action']
        } else {
            params.action = 'show'
        }
        if (attrs['class']) {
            params['class'] = attrs['class']
        }

        if (attrs['item']) {
            out << _getItemLink(params, attrs['item'], body().toString())
        } else {
            out << ''
        }
    }

    private String _getItemLink(Map params, Item item, String body) {
        def stringCleaner = _getStringCleaner()
        params.controller = 'item'
        params.id = item.id
        params.params = [title: stringCleaner.clean(item.title)]
        return _execGLink(params, body)
    }

    private String _getItemLink(Map params, SolrDocument item, String body) {
        def stringCleaner = _getStringCleaner()
        params.controller = 'item'
        params.id = Long.parseLong(item.id)
        params.params = [title: stringCleaner.clean(item.title)]
        return _execGLink(params, body)
    }

    def _getStringCleaner() {
        if (!_stringCleaner) {
            _stringCleaner = new StringCleaner()
        }
        return _stringCleaner
    }

    /**
     * Only use this for dashboard.  Full myItems list should be done in BrowseController.
     */
    def myItems = { attrs ->
        def listParams = [offset:0, max:5, showDeleted:false]
        Map myItems = browseService.myItems(listParams, springSecurityService.currentUser)
        Map linkParams = [action:'show']

        if (myItems.totalCount == 0) {
            out << "You haven't published any items yet.  Maybe you should go do that now."
        } else {
            out << '<ul class="item-list">'
            for (itemInstance in myItems.results) {
                out << '<li>\n'
                out << '    <strong>' << _getItemLink(linkParams, itemInstance, itemInstance.title) << '</strong>\n<br/>\n'
                out << '    <span class="date full-date">\n'
                out << '        <span class="alt-font">Published</span> ' << joda.format(value:itemInstance.published, style:'L-') << '\n'
                out << '    </span>\n'
                out << '</li>\n'

            }
            out << '</ul>'
            out << g.link(controller:'browse', action:'my_items') {'More...' }
        }
    }

    /**
     * Mockable for unit testing
     * TODO: find a clean way to mock g:link
     */
    private String _execGLink(String action, Long id, String urlTitle, String body) {
        return g.link([controller:'item', action:action, id:id, params:[title: urlTitle]], body)
    }

    /**
     * Mockable for unit testing
     * TODO: find a clean way to mock g:link
     *
     * @param params
     * @return
     */
    private String _execGLink(Map params, String body) {
        return g.link(params) { body }
    }

    def _getDataFormatter() {
        if (!_dataFormatter) {
            _dataFormatter = new DataFormatter()
        }
        return _dataFormatter
    }

}
